package com.fancy.recommendation.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fancy.recommendation.dto.request.UserBehaviorRequestDTO;
import com.fancy.recommendation.entity.UserBehavior;
import com.fancy.recommendation.entity.UserPreference;
import com.fancy.recommendation.mapper.UserBehaviorMapper;
import com.fancy.recommendation.mapper.UserPreferenceMapper;
import com.fancy.recommendation.service.UserBehaviorService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 用户行为分析服务实现类
 *
 * @author fancy
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserBehaviorServiceImpl implements UserBehaviorService {

    private final UserBehaviorMapper userBehaviorMapper;
    private final UserPreferenceMapper userPreferenceMapper;
    private final StringRedisTemplate stringRedisTemplate;
    private final ObjectMapper objectMapper;

    @Value("${recommendation.algorithm.behavior-weights.play:1.0}")
    private Double playWeight;

    @Value("${recommendation.algorithm.behavior-weights.like:2.0}")
    private Double likeWeight;

    @Value("${recommendation.algorithm.behavior-weights.collect:3.0}")
    private Double collectWeight;

    @Value("${recommendation.algorithm.behavior-weights.share:2.5}")
    private Double shareWeight;

    @Value("${recommendation.algorithm.behavior-weights.comment:1.5}")
    private Double commentWeight;

    @Override
    @Transactional
    public void recordBehavior(UserBehaviorRequestDTO request) {
        try {
            UserBehavior behavior = new UserBehavior();
            behavior.setUserId(request.getUserId());
            behavior.setActionType(request.getActionType());
            behavior.setTargetId(request.getTargetId());
            behavior.setTargetType(request.getTargetType());
            behavior.setBehaviorValue(calculateBehaviorWeight(request.getActionType(), request.getBehaviorValue()));
            behavior.setContextInfo(objectMapper.writeValueAsString(request.getContextInfo()));
            behavior.setCreateTime(LocalDateTime.now());

            userBehaviorMapper.insert(behavior);

            // 异步更新用户偏好
            updateUserPreferenceAsync(request.getUserId());

            log.info("记录用户行为成功: userId={}, action={}, target={}:{}",
                request.getUserId(), request.getActionType(), request.getTargetType(), request.getTargetId());

        } catch (Exception e) {
            log.error("记录用户行为失败", e);
            throw new RuntimeException("记录用户行为失败", e);
        }
    }

    @Override
    @Transactional
    public void batchRecordBehaviors(List<UserBehaviorRequestDTO> requests) {
        if (requests == null || requests.isEmpty()) {
            return;
        }

        List<UserBehavior> behaviors = requests.stream().map(request -> {
            UserBehavior behavior = new UserBehavior();
            behavior.setUserId(request.getUserId());
            behavior.setActionType(request.getActionType());
            behavior.setTargetId(request.getTargetId());
            behavior.setTargetType(request.getTargetType());
            behavior.setBehaviorValue(calculateBehaviorWeight(request.getActionType(), request.getBehaviorValue()));
            try {
                behavior.setContextInfo(objectMapper.writeValueAsString(request.getContextInfo()));
            } catch (Exception e) {
                behavior.setContextInfo("{}");
            }
            behavior.setCreateTime(LocalDateTime.now());
            return behavior;
        }).collect(Collectors.toList());

        // 批量插入
        for (UserBehavior behavior : behaviors) {
            userBehaviorMapper.insert(behavior);
        }

        // 批量更新用户偏好
        Set<Long> userIds = requests.stream().map(UserBehaviorRequestDTO::getUserId).collect(Collectors.toSet());
        userIds.forEach(this::updateUserPreferenceAsync);

        log.info("批量记录用户行为成功: 数量={}", behaviors.size());
    }

    @Override
    public Map<String, Object> getUserBehaviorStats(Long userId) {
        String cacheKey = "user:behavior:stats:" + userId;

        try {
            String cached = stringRedisTemplate.opsForValue().get(cacheKey);
            if (cached != null) {
                return objectMapper.readValue(cached, Map.class);
            }
        } catch (Exception e) {
            log.warn("获取缓存的用户行为统计失败", e);
        }

        List<Map<String, Object>> stats = userBehaviorMapper.getUserBehaviorStats(userId);
        Map<String, Object> result = new HashMap<>();

        for (Map<String, Object> stat : stats) {
            result.put((String) stat.get("action_type"), stat);
        }

        // 缓存结果
        try {
            stringRedisTemplate.opsForValue().set(cacheKey, objectMapper.writeValueAsString(result), 1, TimeUnit.HOURS);
        } catch (Exception e) {
            log.warn("缓存用户行为统计失败", e);
        }

        return result;
    }

    @Override
    public List<UserBehavior> getRecentBehaviors(Long userId, Integer limit) {
        if (limit == null || limit <= 0) {
            limit = 20;
        }
        return userBehaviorMapper.getRecentBehaviors(userId, limit);
    }

    @Override
    @Transactional
    public void analyzeUserPreferences(Long userId) {
        // 获取用户行为数据
        LambdaQueryWrapper<UserBehavior> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserBehavior::getUserId, userId)
               .ge(UserBehavior::getCreateTime, LocalDateTime.now().minusDays(30));

        List<UserBehavior> behaviors = userBehaviorMapper.selectList(wrapper);

        if (behaviors.isEmpty()) {
            return;
        }

        // 分析偏好
        Map<String, Map<String, BigDecimal>> preferences = new HashMap<>();

        for (UserBehavior behavior : behaviors) {
            String preferenceType = behavior.getTargetType();
            String preferenceValue = behavior.getTargetId().toString();

            preferences.computeIfAbsent(preferenceType, k -> new HashMap<>())
                      .merge(preferenceValue, behavior.getBehaviorValue(), BigDecimal::add);
        }

        // 保存用户偏好
        List<UserPreference> userPreferences = new ArrayList<>();
        for (Map.Entry<String, Map<String, BigDecimal>> typeEntry : preferences.entrySet()) {
            String preferenceType = typeEntry.getKey();

            for (Map.Entry<String, BigDecimal> valueEntry : typeEntry.getValue().entrySet()) {
                UserPreference preference = new UserPreference();
                preference.setUserId(userId);
                preference.setPreferenceType(preferenceType);
                preference.setPreferenceValue(valueEntry.getKey());
                preference.setWeight(valueEntry.getValue());
                preference.setUpdateTime(LocalDateTime.now());

                userPreferences.add(preference);
            }
        }

        if (!userPreferences.isEmpty()) {
            userPreferenceMapper.batchInsertOrUpdate(userPreferences);
        }

        log.info("分析用户偏好完成: userId={}, 偏好数量={}", userId, userPreferences.size());
    }

    @Override
    public List<UserPreference> getUserPreferences(Long userId) {
        return userPreferenceMapper.getUserPreferences(userId);
    }

    @Override
    public List<Map<String, Object>> getHotContent(String actionType, Integer days, Integer limit) {
        if (days == null || days <= 0) {
            days = 7;
        }
        if (limit == null || limit <= 0) {
            limit = 50;
        }

        LocalDateTime startTime = LocalDateTime.now().minusDays(days);
        return userBehaviorMapper.getHotContentByAction(actionType, startTime, limit);
    }

    @Override
    public Double calculateUserSimilarity(Long userId1, Long userId2) {
        // 获取两个用户的行为数据
        List<UserBehavior> behaviors1 = getRecentBehaviors(userId1, 1000);
        List<UserBehavior> behaviors2 = getRecentBehaviors(userId2, 1000);

        if (behaviors1.isEmpty() || behaviors2.isEmpty()) {
            return 0.0;
        }

        // 构建用户行为向量
        Map<String, BigDecimal> vector1 = buildBehaviorVector(behaviors1);
        Map<String, BigDecimal> vector2 = buildBehaviorVector(behaviors2);

        // 计算余弦相似度
        return calculateCosineSimilarity(vector1, vector2);
    }

    @Override
    public List<Map<String, Object>> getSimilarUsers(Long userId, Integer limit) {
        // 获取用户最近的目标行为
        List<UserBehavior> userBehaviors = getRecentBehaviors(userId, 100);

        if (userBehaviors.isEmpty()) {
            return Collections.emptyList();
        }

        List<Long> targetIds = userBehaviors.stream()
            .map(UserBehavior::getTargetId)
            .distinct()
            .collect(Collectors.toList());

        // 这里应该调用UserBehaviorMapper的相应方法
        // 暂时返回空列表，实际实现需要添加对应的SQL查询
        return Collections.emptyList();
    }

    @Override
    @Transactional
    public void cleanExpiredBehaviors(Integer days) {
        if (days == null || days <= 0) {
            days = 90; // 默认清理90天前的数据
        }

        LocalDateTime expireTime = LocalDateTime.now().minusDays(days);
        int deletedCount = userBehaviorMapper.deleteExpiredBehaviors(expireTime);

        log.info("清理过期行为数据完成: 清理了{}条记录", deletedCount);
    }

    /**
     * 计算行为权重
     */
    private BigDecimal calculateBehaviorWeight(String actionType, BigDecimal originalValue) {
        Double weight = getActionWeight(actionType);
        return originalValue.multiply(BigDecimal.valueOf(weight));
    }

    /**
     * 获取行为类型权重
     */
    private Double getActionWeight(String actionType) {
        switch (actionType.toLowerCase()) {
            case "play":
                return playWeight;
            case "like":
                return likeWeight;
            case "collect":
                return collectWeight;
            case "share":
                return shareWeight;
            case "comment":
                return commentWeight;
            default:
                return 1.0;
        }
    }

    /**
     * 异步更新用户偏好
     */
    private void updateUserPreferenceAsync(Long userId) {
        // 这里可以使用Spring的@Async注解或者消息队列来异步处理
        try {
            analyzeUserPreferences(userId);
        } catch (Exception e) {
            log.error("异步更新用户偏好失败: userId={}", userId, e);
        }
    }

    /**
     * 构建行为向量
     */
    private Map<String, BigDecimal> buildBehaviorVector(List<UserBehavior> behaviors) {
        Map<String, BigDecimal> vector = new HashMap<>();

        for (UserBehavior behavior : behaviors) {
            String key = behavior.getTargetType() + ":" + behavior.getTargetId();
            vector.merge(key, behavior.getBehaviorValue(), BigDecimal::add);
        }

        return vector;
    }

    /**
     * 计算余弦相似度
     */
    private Double calculateCosineSimilarity(Map<String, BigDecimal> vector1, Map<String, BigDecimal> vector2) {
        Set<String> commonKeys = new HashSet<>(vector1.keySet());
        commonKeys.retainAll(vector2.keySet());

        if (commonKeys.isEmpty()) {
            return 0.0;
        }

        double dotProduct = 0.0;
        double norm1 = 0.0;
        double norm2 = 0.0;

        for (String key : commonKeys) {
            BigDecimal value1 = vector1.get(key);
            BigDecimal value2 = vector2.get(key);

            dotProduct += value1.doubleValue() * value2.doubleValue();
        }

        for (BigDecimal value : vector1.values()) {
            norm1 += Math.pow(value.doubleValue(), 2);
        }

        for (BigDecimal value : vector2.values()) {
            norm2 += Math.pow(value.doubleValue(), 2);
        }

        if (norm1 == 0.0 || norm2 == 0.0) {
            return 0.0;
        }

        return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
    }
}
